#include <jni.h>
#include <string>
#include <fcntl.h>
#include <unistd.h>
//引入mmap的头文件  mmap是系统函数
#include "sys/mman.h"
#include "android/log.h"

// 定义日志标签
#define LOG_TAG "NativeLog"

// 定义日志宏
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)


extern "C" JNIEXPORT jstring JNICALL
Java_com_example_binder_MainActivity_callNativeMmap
        (JNIEnv *env, jobject) {
    std::string file = "/sdcard/binder";
    int m_fd = open(file.c_str(), O_RDWR | O_CREAT, S_IRWXU);
    /**
     * mmap函数的做了如下事情：
     * setup 1.在物理内存中开辟了4096个字节的内存大小
     * setup 2.将物理内存与磁盘进行关联
     * setup 3.MMU将mmap开辟的物理内存地址，转换成虚拟地址
     */
    //mmap函数返回的是虚拟地址
    mmap(0, 4096, PROT_EXEC, MAP_SHARED, m_fd, 0);

    //假设APP2想要与APP1通信，一次通信超过1M-8K的数据，可以通信吗？ 答：不能，因为Binder中mmap函数创建的物理内存大小只有1M-8K
}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_binder_XunuaBinder_write(JNIEnv *env, jobject thiz) {
//    std::string file = "/sdcard/binder";
//    int m_fd = open(file.c_str(), O_RDWR | O_CREAT, S_IRWXU);
//    //指定磁盘文件大小为4096,填充0。
//    ftruncate(m_fd,4096);
//    //物理内存与磁盘家里映射关系，*m_ptr 为虚拟地址。
//    int8_t* m_ptr = static_cast<int8_t *>(mmap(0, 4096, PROT_EXEC, MAP_SHARED, m_fd, 0));
////    //拿到虚拟地址，修改内部的值为10
////    *m_ptr=10;
//    LOGE("打印日志： %p    m_fd:%d   错误信息:%s",m_ptr,m_fd);
//
//    std::string data("你妈让我给你带句话");
//    memcpy(m_ptr, data.data(), data.size());


    std::string file = "/sdcard/binder";
    // 打开文件
    int m_fd = open(file.c_str(), O_RDWR | O_CREAT, S_IRWXU);
    if (m_fd == -1) {
        LOGE("Failed to open file: %s", strerror(errno));
        return;
    }
    // 指定磁盘文件大小为4096，并填充0
    if (ftruncate(m_fd, 4096) == -1) {
        LOGE("Failed to set file size: %s", strerror(errno));
        close(m_fd);
        return;
    }
    // 进行内存映射，使用 PROT_READ | PROT_WRITE 而非 PROT_EXEC
    int8_t* m_ptr = static_cast<int8_t*>(mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0));
    if (m_ptr == MAP_FAILED) {
        LOGE("mmap failed: %s", strerror(errno));
        close(m_fd);
        return;
    }

    LOGE("打印日志： %p    m_fd: %d", m_ptr, m_fd);

    // 拿到虚拟地址，写入数据
    std::string data("你妈让我给你带句话");
    memcpy(m_ptr, data.data(), data.size());

    // 解除内存映射和关闭文件
    if (munmap(m_ptr, 4096) == -1) {
        LOGE("munmap failed: %s", strerror(errno));
    }

    close(m_fd);
}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_binder_XunuaBinder_read(JNIEnv *env, jobject thiz) {
    std::string file = "/sdcard/binder";
    int m_fd = open(file.c_str(), O_RDWR | O_CREAT, S_IRWXU);
    //指定磁盘文件大小为4096,填充0。
    ftruncate(m_fd,4096);
    //因为起始地址一致，大小一致，文件fd一致，所以mmap会返回上次映射时的同一个虚拟地址
    void* m_ptr = static_cast<int8_t *>(mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0));

    //读取数据
    char* buf= static_cast<char *>(malloc(100));
    //拷贝前100个字节
    memcpy(buf,m_ptr,100);
    //关闭虚拟地址m_ptr的映射
    munmap(m_ptr,4096);

    std::string result(buf);
    //关闭文件描述符
    close(m_fd);
    return env->NewStringUTF(result.c_str());
}